
#include "c_controller.h"
#include "c_net.h"


//rsp_classdef<rsp_c_objlist::cliclassdata> rsp_c_objlist::_classreg;
rsp_c_settings  RSPC_SETTINGS;

uint rsp_c_net::MSEC_SYNC_REPLY_TIMEOUT         = 500;
uint rsp_c_net::MSEC_CONNECT_REPLY_TIMEOUT      = 500;
uint rsp_c_net::N_RETRY_AFTER_TIMEOUT           = 3;
uint rsp_c_net::N_RETRY_AFTER_FAILSYNC          = 10;
uint rsp_c_net::TIME_BETWEEN_SYNC               = 16000;
uint rsp_c_net::CLIENT_TIMEOUT_FRAMES           = 400;

////////////////////////////////////////////////////////////////////////////////
rsp_c_object_state* rsp_c_object_state::get_from_oid( uint oid )
{
    DASSERT( Gactive_controller->client_side() );
    return ((rsp_c_controller*)Gactive_controller)->find_object(oid);
}

////////////////////////////////////////////////////////////////////////////////
bool rsp_c_net::process_frame()
{
    uint ct = _Timer.time();
    if( (!_bsynctimer && int(ct - _time_lastsync) >= (int)TIME_BETWEEN_SYNC)
        || (_bsynctimer && int(ct - _syncreq_time) >= (int)MSEC_SYNC_REPLY_TIMEOUT) )
    {
        if( is_connected() )
            send_sync();
    }

    if( !is_connected() )
    {
        sysMilliSecondSleep(1000/30);
        return true;
    }

    //do not sleep if we're awaiting sync reply
    //if( _bsynctimer == false )

    int time = _Timer.usec_to_tick( _ctrl.get_bound_state_id()+1 );
    if( time>=1000 )
    {
        sysMilliSecondSleep(1);
        int time2 = _Timer.usec_to_tick( _ctrl.get_bound_state_id()+1 );
        if( time-time2 >= 2000 ) {
            LOG(2) {
                *TLOG << "1ms sleep lasting " << (float(time-time2)/1000) << "ms" << BINSTREAM_FLUSH;
            }
        }
    }
    else if( time>0 )
        sysMilliSecondSleep(0);
    else if( time < -1000  &&  _patched )
    {
        LOG(2) {
            *TLOG << timefmt(Gactive_controller->get_current_stateid())
            << " frame late by " << (-float(time)/1000) << "ms" << BINSTREAM_FLUSH;
        }
    }


    //receive all available data
    //TODO: maybe break if expected data was received, although clearing
    // the input buffers could be nice
    int rps=0;
    int np=0;
    uint msz;
    while( (msz = _net.data_available(0)) > 0 )
    {
        //DASSERT( msz < 3000 || msz > 4000 );
        uint t1 = _Timer.time();

        uint8 ty;
        _net >> ty;

        if( ty == RSP_PACKET::DISCONNECT )
        {
            _net.acknowledge();
            disconnect();

            return false;
        }
        else if( ty == RSP_PACKET::SYNC  &&  _bsynctimer )
        {
            uint32 rsid;
            _net >> rsid;
            if( rsid != _syncreq_id )   { _net.acknowledge(true); continue; }

            recv_sync(false);
            continue;
        }

        DASSERT( ty == RSP_PACKET::DIFF );
        if( ty != RSP_PACKET::DIFF )  { _net.acknowledge(true); continue; }

        uint tots = _net.data_size();

        uint tmsg;
        _net >> tmsg;       //server time when message was sent

        if( _bcanmeasureping )
        {
            //compute current time on server
            uint cst = t1 - CLIENT_AHEAD(_frame_usec);

            //ignore extreme values
            if( int(cst-tmsg) < 4*(int)_trip_delay )
                _trip_delay = 3*_trip_delay/4 + 2*int(cst-tmsg)/4;
        }

        int ps = _ctrl.patch( _net, _ssid, _slot );
        _net.acknowledge(ps<0);
        uint t2 = _Timer.time();

        if( ps >= 0 )
            ++_patched;

        timefmt cs( _ctrl.get_ref_state_id() );

        if(t2-t1 > 1) {
            LOG(2) {
                *TLOG << cs << " patching duration: " << (t2-t1) << "ms" << BINSTREAM_FLUSH;
            }
        }

        if( tots > 55 ) {
            LOG(2) {
                *TLOG << cs << " patch (" <<  tots << ") received at " << timefmt();
                if(ps==0)
                    *TLOG << " (old)";
                *TLOG << BINSTREAM_FLUSH;
            }
        }
        ++np;

        if( ps > rps )
            rps = ps;
    }

    uint curframe = _Timer.ticks();
    if( int(curframe-_ctrl.get_ref_state_id()) > (int)CLIENT_TIMEOUT_FRAMES )
    {
        if( is_connected() )
            disconnect_silent();

        sysMilliSecondSleep(1000/30);
        return false;
    }   

    _bcanmeasureping = true;

    timefmt cs( _ctrl.get_ref_state_id() );

    if(np>1) {
        LOG(2) {
            *TLOG << cs << " data patched " << np << " times" << BINSTREAM_FLUSH;
        }
    }

    if( rps > 0 )
    {
        LOG(1) { *TLOG << cs << (rps>1 ? " initial resync ":" resync "); }

        uint t1 = _Timer.time();
        pre_resync( rps>1 );
        _ctrl.resync();
        resync();

        LOG(1) {
            uint t2 = _Timer.time();
            *TLOG << (t2-t1) << "ms" << BINSTREAM_FLUSH;
        }

        //resync may have taken too much time, reset the sync flag
        _bsynctimer = _bcanmeasureping = false;
    }

    //and update the bound objects to it
    uint tck = _Timer.ticks();

    uint t0 = _Timer.time();
    int inp = _ctrl.interpolate( tck, _net, _ssid, _slot );

    //if(inp)
        //interpolation may have taken too much time, reset the sync flag
        //_bcanmeasureping = false;

    //drawing the world usually takes more time, and if we are expecting a sync reply,
    // precise time of arrival may not be determined accurately
    // so we should reset the _bsynctimer flag
    if( inp && (!_bdrawratehalf  || (tck&1)==0) )
    {
        uint t1 = _Timer.time();
        uint tck2 = _bdrawratehalf  ?  tck+2 : tck+1;

        int time2 = _Timer.usec_to_tick(tck2);
        if( time2 >= 1000 )//(int)_Timer.get_period_usec() )
        {
            draw_world();
            uint t2 = _Timer.time();
            if( t2-t1 > 4 ) {
                LOG(2) {
                    *TLOG << timefmt(tck) << " frame rendered in " << (t2-t1) << "ms, interpolated in " << (t1-t0)
                    << "ms, ending at " << timefmt() << BINSTREAM_FLUSH;
                }
            }
            
            return true;
        }
        else if( t1-t0>1 ) {
            LOG(2) {
                *TLOG << "frame " << timefmt(tck) << " render skipped, interpolated in " << (t1-t0) << "ms" << BINSTREAM_FLUSH;
            }
        }

        //if no message arrived while drawing, we don't have to reset the _bsynctimer flag
        if( _net.data_available(0) )
            _bcanmeasureping = _bsynctimer = false;
    }

    return false;
}

